package settings.Action;

import logging.*;
import robot.*;

import java.util.*;
import java.util.regex.*;
import javax.swing.*;

/**
 * Represents a single action to be performed during an automated test.
 * @author BDS
 */
public enum Action
{
	PAUSE
	{
		/**
		 * Returns a string specific to the Action.
		 * @return A string specific to the Action.
		 */
		@Override
		public String toString()
		{
			return "Pause";
		}

		/**
		 * Initiates a {@link Thread#sleep(long) sleep} for the number of milliseconds defined in the arguments.
		 * @param executionRobot The Robot API used to execute the action.
		 * @param arguments The relevant arguments: {@link Thread#sleep(long)} millisecond interval.
		 * @return <code>true</code> if successful; <code>false</code> otherwise.
		 */
		@Override
		public boolean execute(ExecutionRobot executionRobot, List<String> arguments)
		{
			boolean success = false;
			
			if (arguments != null && arguments.size() > 0)
			{
				try
				{
					Thread.sleep(Long.parseLong(arguments.get(0)));
					success = true;
				}
				catch (Exception exception)
				{
					Logger.logException(exception);
				}
			}
			
			return success;
		}
	},
	KEYSTROKE
	{
		/**
		 * Returns a string specific to the Action.
		 * @return A string specific to the Action.
		 */
		@Override
		public String toString()
		{
			return "Press keys";
		}

		/**
		 * Presses and releases the keys defined in the arguments.
		 * @param executionRobot The Robot API used to execute the action.
		 * @param arguments The relevant keys to press.
		 * @return <code>true</code> if successful; <code>false</code> otherwise.
		 */
		@Override
		public boolean execute(ExecutionRobot executionRobot, List<String> arguments)
		{
			boolean success = false;
			
			if (arguments != null && arguments.size() > 0)
			{
				Integer[] keyCodes = new Integer[arguments.size()];
				for (int i = 0; i < arguments.size(); i++)
				{
					if (arguments.get(i) != null && !arguments.get(i).isEmpty())
					{
						keyCodes[i] = KeyStroke.getKeyStroke(arguments.get(i)).getKeyCode();
					}
				}
				
				executionRobot.keyStroke(keyCodes);
				
				success = true;
			}
			
			return success;
		}
	},
	MOUSEMOVE
	{
		/**
		 * Returns a string specific to the Action.
		 * @return A string specific to the Action.
		 */
		@Override
		public String toString()
		{
			return "Move mouse";
		}

		/**
		 * Moves the mouse to the location defined in the arguments.
		 * @param executionRobot The Robot API used to execute the action.
		 * @param arguments The location to move the mouse to.
		 * @return <code>true</code> if successful; <code>false</code> otherwise.
		 */
		@Override
		public boolean execute(ExecutionRobot executionRobot, List<String> arguments)
		{
			boolean success = false;
			
			if (arguments != null && 
				arguments.size() > 0 && 
				arguments.get(0) != null && 
				!arguments.get(0).isEmpty()) 
			{
				if (MouseMoveTarget.fromName(arguments.get(0)) == MouseMoveTarget.COORDINATES &&
					arguments.size() > 2)
				{
					int x = Integer.parseInt(arguments.get(1));
					int y = Integer.parseInt(arguments.get(2));
					executionRobot.mouseMove(x, y);
					
					success = true;
				}
				else if (MouseMoveTarget.fromName(arguments.get(0)) == MouseMoveTarget.IMAGE &&
						 arguments.size() > 1)
				{
					success = executionRobot.checkForImage(arguments.get(1), true);
				}
			}

			return success;
		}
	},
	MOUSECLICK
	{
		/**
		 * Returns a string specific to the Action.
		 * @return A string specific to the Action.
		 */
		@Override
		public String toString()
		{
			return "Click mouse";
		}

		/**
		 * Presses and releases the Mouse1 button.
		 * @param executionRobot The Robot API used to execute the action.
		 * @param arguments Irrelevant.
		 * @return <code>true</code> if successful; <code>false</code> otherwise.
		 */
		@Override
		public boolean execute(ExecutionRobot executionRobot, List<String> arguments)
		{
			boolean success = false;
			
			executionRobot.clickMouse();
			success = true;
			
			return success;
		}
	},
	TEXTENTRY
	{
		/**
		 * Returns a string specific to the Action.
		 * @return A string specific to the Action.
		 */
		@Override
		public String toString()
		{
			return "Enter text";
		}
		
		/**
		 * Enters the text defined in the arguments.
		 * @param executionRobot The Robot API used to execute the action.
		 * @param arguments The text to enter.
		 * @return <code>true</code> if successful; <code>false</code> otherwise.
		 */
		@Override
		public boolean execute(ExecutionRobot executionRobot, List<String> arguments)
		{
			boolean success = false;
			
			if (arguments != null && arguments.size() == 1)
			{
				executionRobot.type(arguments.get(0));
				success = true;
			}
			
			return success;
		}
	},
	LOG
	{
		/**
		 * Returns a string specific to the Action.
		 * @return A string specific to the Action.
		 */
		@Override
		public String toString()
		{
			return "Write log message";
		}
		
		/**
		 * Writes the specified text to the log.txt file.
		 * @param executionRobot The Robot API used to execute the action.
		 * @param arguments The text to log.
		 * @return <code>true</code> if successful; <code>false</code> otherwise.
		 */
		@Override
		public boolean execute(ExecutionRobot executionRobot, List<String> arguments)
		{
			boolean success = false;
			
			if (arguments != null && arguments.size() > 0)
			{
				for (String message : arguments)
				{
					Logger.logMessage(message);
				}
				
				success = true;
			}
			
			return success;
		}
	},
	VALIDATE
	{
		/**
		 * Returns a string specific to the Action.
		 * @return A string specific to the Action.
		 */
		@Override
		public String toString()
		{
			return "Validate";
		}
		
		/**
		 * Verifies the specified conditions have been met.
		 * @param executionRobot The Robot API used to execute the action.
		 * @param arguments The conditions to verify.
		 * @return <code>true</code> if successful; <code>false</code> otherwise.
		 */
		@Override
		public boolean execute(ExecutionRobot executionRobot, List<String> arguments)
		{
			boolean success = super.execute(executionRobot, arguments);
			
			if (arguments != null && arguments.size() > 2)
			{
				switch (ValidateTarget.fromName(arguments.get(0)))
				{
				case ACTIVEPROCESS:
					success = Pattern.matches(executionRobot.wildcardToRegex(arguments.get(1)), executionRobot.getActiveWindowProcess()) ^ Boolean.parseBoolean(arguments.get(2));
					
					// Provide an indication of the failure.
					if (!success)
					{
						String message = "Active process \"" + executionRobot.getActiveWindowProcess() + "\" ";
						if (Boolean.parseBoolean(arguments.get(2)))
						{
							message += "matches \"";
						}
						else
						{
							message += "does not match \"";
						}
						message += arguments.get(1) + "\"";
						
						Logger.logMessage(message);
					}
					break;
					
				case ACTIVEWINDOW:
					success = Pattern.matches(executionRobot.wildcardToRegex(arguments.get(1)), executionRobot.getActiveWindowTitle()) ^ Boolean.parseBoolean(arguments.get(2));

					// Provide an indication of the failure.
					if (!success)
					{
						String message = "Active window \"" + executionRobot.getActiveWindowTitle() + "\" ";
						if (Boolean.parseBoolean(arguments.get(2)))
						{
							message += "matches \"";
						}
						else
						{
							message += "does not match \"";
						}
						message += arguments.get(1) + "\"";
						
						Logger.logMessage(message);
					}
					break;
					
				case IMAGE:
					success = executionRobot.checkForImage(arguments.get(1), false) ^ Boolean.parseBoolean(arguments.get(2));

					// Provide an indication of the failure.
					if (!success)
					{
						String message = "Image \"" + arguments.get(1) + "\" ";
						if (Boolean.parseBoolean(arguments.get(2)))
						{
							message += "is visible";
						}
						else
						{
							message += "is not visible";
						}
						
						Logger.logMessage(message);
					}
					break;
					
				case WINDOW:
					for (String windowTitle : executionRobot.getWindowTitles())
					{
						success = Pattern.matches(executionRobot.wildcardToRegex(arguments.get(1)), windowTitle);
						if (success)
						{
							break;
						}
					}
					success ^= Boolean.parseBoolean(arguments.get(2));

					// Provide an indication of the failure.
					if (!success)
					{
						String message = "\"" + arguments.get(1) + "\" ";
						if (Boolean.parseBoolean(arguments.get(2)))
						{
							message += "is present";
						}
						else
						{
							message += "is not present";
						}
						
						Logger.logMessage(message);
					}
					break;
				}
			}
			
			return success;
		}
	};
	
	/**
	 * Executes a user action using the given arguments.
	 * @param executionRobot The Robot API used to execute the action.
	 * @param arguments Arguments relevant to the specific user action.
	 */
	public boolean execute(ExecutionRobot executionRobot, List<String> arguments)
	{
		return true;
	}
}
